package devcontainer

import (
	"archive/tar"
	"bytes"
	"context"
	"fmt"
	"io"
	"os/exec"
	"regexp"
	"strings"
	"time"

	"code.gitea.io/gitea/models/db"
	devcontainer_models "code.gitea.io/gitea/models/devcontainer"
	"code.gitea.io/gitea/models/repo"
	"code.gitea.io/gitea/models/user"
	docker_module "code.gitea.io/gitea/modules/docker"
	"code.gitea.io/gitea/modules/git"
	"code.gitea.io/gitea/modules/log"
	"code.gitea.io/gitea/modules/setting"
	gitea_context "code.gitea.io/gitea/services/context"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/api/types/filters"
	"github.com/docker/docker/client"
	"github.com/docker/docker/errdefs"
	"github.com/docker/go-connections/nat"
)

func GetDevContainerStatusFromDocker(ctx context.Context, containerName string) (string, error) {
	// 创建docker client
	cli, err := docker_module.CreateDockerClient(ctx)
	if err != nil {
		return "", err
	}
	defer cli.Close()
	containerID, err := docker_module.GetContainerID(cli, containerName)
	if err != nil {
		return "", err
	}
	containerStatus, err := docker_module.GetContainerStatus(cli, containerID)
	if err != nil {
		return "", err
	}
	return containerStatus, nil
}
func CreateDevContainerByDockerAPI(ctx context.Context, newDevcontainer *devcontainer_models.Devcontainer, imageName string, repo *repo.Repository, publicKeyList []string) error {
	dbEngine := db.GetEngine(ctx)
	configurationString, err := GetDevcontainerConfigurationString(ctx, repo)
	if err != nil {
		return err
	}
	configurationModel, err := UnmarshalDevcontainerConfigContent(configurationString)
	if err != nil {
		return err
	}
	dockerSocket, err := docker_module.GetDockerSocketPath()
	if err != nil {
		return err
	}

	_, err = dbEngine.Table("devcontainer").
		Where("user_id = ? AND repo_id = ? ", newDevcontainer.UserId, newDevcontainer.RepoId).
		Update(&devcontainer_models.Devcontainer{DevcontainerStatus: 1})
	if err != nil {
		log.Info("err %v", err)
	}
	cli, err := docker_module.CreateDockerClient(ctx)
	if err != nil {
		return err
	}
	if configurationModel.Build == nil || configurationModel.Build.Dockerfile == "" {
		script := "docker " + "-H " + dockerSocket + " pull " + configurationModel.Image
		cmd := exec.Command("sh", "-c", script)
		err = cmd.Start()
		if err != nil {
			return err
		}
	}

	_, err = dbEngine.Table("devcontainer").
		Where("user_id = ? AND repo_id = ? ", newDevcontainer.UserId, newDevcontainer.RepoId).
		Update(&devcontainer_models.Devcontainer{DevcontainerStatus: 2})
	if err != nil {
		return err
	}
	docker_module.CreateAndStartContainer(ctx, cli, imageName,
		[]string{
			"sh",
			"-c",
			"tail -f /dev/null;",
		},
		nil,
		nil,
		nat.PortSet{
			nat.Port("22/tcp"): {},
		},
		newDevcontainer.Name)
	_, err = dbEngine.Table("devcontainer").
		Where("user_id = ? AND repo_id = ? ", newDevcontainer.UserId, newDevcontainer.RepoId).
		Update(&devcontainer_models.Devcontainer{DevcontainerStatus: 3})
	if err != nil {
		return err
	}

	output, err := docker_module.ExecCommandInContainer(ctx, cli, newDevcontainer.Name,
		`echo "`+newDevcontainer.DevcontainerHost+` host.docker.internal" | tee -a /etc/hosts;apt update;apt install -y git ;git clone `+strings.TrimSuffix(setting.AppURL, "/")+repo.Link()+"  "+newDevcontainer.DevcontainerWorkDir+"/"+repo.Name+`; apt install -y ssh;echo "PubkeyAuthentication yes `+"\n"+`PermitRootLogin yes `+"\n"+`" | tee -a /etc/ssh/sshd_config;rm -f /etc/ssh/ssh_host_*; ssh-keygen -A; service ssh restart;mkdir -p ~/.ssh;chmod 700 ~/.ssh;echo "`+strings.Join(publicKeyList, "\n")+`" > ~/.ssh/authorized_keys;chmod 600 ~/.ssh/authorized_keys;`,
	)
	if err != nil {
		return err
	}
	log.Info(output)

	_, err = dbEngine.Table("devcontainer").
		Where("user_id = ? AND repo_id = ? ", newDevcontainer.UserId, newDevcontainer.RepoId).
		Update(&devcontainer_models.Devcontainer{DevcontainerStatus: 4})
	if err != nil {
		return err
	}
	return nil
}

func CreateDevContainerByDockerCommand(ctx context.Context, newDevcontainer *devcontainer_models.Devcontainer, repo *repo.Repository, publicKeyList []string) (string, error) {

	dbEngine := db.GetEngine(ctx)
	configurationString, err := GetDevcontainerConfigurationString(ctx, repo)
	if err != nil {
		return "", err
	}
	configurationModel, err := UnmarshalDevcontainerConfigContent(configurationString)
	if err != nil {
		return "", err
	}

	var imageName = configurationModel.Image
	dockerSocket, err := docker_module.GetDockerSocketPath()
	if err != nil {
		return "", err
	}
	if configurationModel.Build != nil && configurationModel.Build.Dockerfile != "" {
		dockerfileContent, err := GetFileContentByPath(ctx, repo, ".devcontainer/"+configurationModel.Build.Dockerfile)
		if err != nil {
			return "", err
		}
		// 创建构建上下文（包含Dockerfile的tar包）
		var buf bytes.Buffer
		tw := tar.NewWriter(&buf)
		defer tw.Close()
		// 添加Dockerfile到tar包
		dockerfile := "Dockerfile"
		content := []byte(dockerfileContent)
		header := &tar.Header{
			Name: dockerfile,
			Size: int64(len(content)),
			Mode: 0644,
		}
		if err := tw.WriteHeader(header); err != nil {
			return "", err
		}
		if _, err := tw.Write(content); err != nil {
			return "", err
		}
		// 执行镜像构建
		imageName = fmt.Sprintf("%d", newDevcontainer.UserId) + "-" + fmt.Sprintf("%d", newDevcontainer.RepoId) + "-dockerfile"
		buildOptions := types.ImageBuildOptions{
			Tags: []string{imageName}, // 镜像标签
		}
		cli, err := docker_module.CreateDockerClient(ctx)
		if err != nil {
			return "", err
		}
		buildResponse, err := cli.ImageBuild(
			context.Background(),
			&buf,
			buildOptions,
		)
		if err != nil {
			return "", err
		}
		output, err := io.ReadAll(buildResponse.Body)
		if err != nil {
			return "", err
		}
		log.Info(string(output))

	}

	// 拉取镜像的命令
	if _, err := dbEngine.Table("devcontainer_output").Insert(&devcontainer_models.DevcontainerOutput{
		Output:         "",
		ListId:         1,
		Status:         "waitting",
		UserId:         newDevcontainer.UserId,
		RepoId:         newDevcontainer.RepoId,
		Command:        "docker " + "-H " + dockerSocket + " pull " + imageName + "\n",
		DevcontainerId: newDevcontainer.Id,
	}); err != nil {
		log.Info("Failed to insert record: %v", err)
		return imageName, err
	}
	cfg, err := setting.NewConfigProviderFromFile(setting.CustomConf)
	if err != nil {
		return imageName, err
	}
	var startCommand string = `docker -H ` + dockerSocket + ` create --restart=always --name ` + newDevcontainer.Name

	// 将每个端口转换为 "-p <port>" 格式
	var portFlags string = " -p 22 "

	exposedPorts := configurationModel.ExtractContainerPorts()
	for _, port := range exposedPorts {
		portFlags = portFlags + fmt.Sprintf(" -p %d ", port)
	}
	startCommand += portFlags

	var envFlags string = ` -e RepoLink="` + strings.TrimSuffix(cfg.Section("server").Key("ROOT_URL").Value(), `/`) + repo.Link() + `" ` +
		` -e DevstarHost="` + newDevcontainer.DevcontainerHost + `"` +
		` -e WorkSpace="` + newDevcontainer.DevcontainerWorkDir + `/` + repo.Name + `" ` +
		` -e DEVCONTAINER_STATUS="start" ` +
		` -e WEB_TERMINAL_HELLO="Successfully connected to the devcontainer" `
	// 遍历 ContainerEnv 映射中的每个环境变量
	for name, value := range configurationModel.ContainerEnv {
		// 将每个环境变量转换为 "-e name=value" 格式
		envFlags = envFlags + fmt.Sprintf(" -e %s=\"%s\" ", name, value)
	}
	startCommand += envFlags
	if configurationModel.Init {
		startCommand += " --init "
	}
	if configurationModel.Privileged {
		startCommand += " --privileged "
	}
	var capAddFlags string
	// 遍历 CapAdd 列表中的每个能力
	for _, capability := range configurationModel.CapAdd {
		// 将每个能力转换为 --cap-add=capability 格式
		capAddFlags = capAddFlags + fmt.Sprintf(" --cap-add %s ", capability)
	}

	startCommand += capAddFlags
	var securityOptFlags string
	// 遍历 SecurityOpt 列表中的每个安全选项
	for _, option := range configurationModel.SecurityOpt {
		// 将每个选项转换为 --security-opt=option 格式
		securityOptFlags = securityOptFlags + fmt.Sprintf(" --security-opt %s ", option)
	}
	startCommand += securityOptFlags
	startCommand += " " + strings.Join(configurationModel.ExtractMountFlags(), " ") + " "
	if configurationModel.WorkspaceFolder != "" {
		startCommand += fmt.Sprintf(" -w %s ", configurationModel.WorkspaceFolder)
	}
	startCommand += " " + strings.Join(configurationModel.RunArgs, " ") + " "
	overrideCommand := ""
	if !configurationModel.OverrideCommand {
		overrideCommand = `  sh -c "/home/webTerminal.sh" `
		startCommand += ` --entrypoint="" `
	}
	//创建并运行容器的命令
	if _, err := dbEngine.Table("devcontainer_output").Insert(&devcontainer_models.DevcontainerOutput{
		Output:         "",
		Status:         "waitting",
		UserId:         newDevcontainer.UserId,
		RepoId:         newDevcontainer.RepoId,
		Command:        startCommand + imageName + overrideCommand + "\n",
		ListId:         2,
		DevcontainerId: newDevcontainer.Id,
	}); err != nil {
		log.Info("Failed to insert record: %v", err)
		return imageName, err
	}

	if _, err := dbEngine.Table("devcontainer_output").Insert(&devcontainer_models.DevcontainerOutput{
		Output:         "",
		Status:         "waitting",
		UserId:         newDevcontainer.UserId,
		RepoId:         newDevcontainer.RepoId,
		Command:        `docker -H ` + dockerSocket + ` start -a ` + newDevcontainer.Name + "\n",
		ListId:         3,
		DevcontainerId: newDevcontainer.Id,
	}); err != nil {
		log.Info("Failed to insert record: %v", err)
		return imageName, err
	}
	//连接容器的命令
	if _, err := dbEngine.Table("devcontainer_output").Insert(&devcontainer_models.DevcontainerOutput{
		Output:         "",
		Status:         "waitting",
		UserId:         newDevcontainer.UserId,
		RepoId:         newDevcontainer.RepoId,
		Command:        `docker -H ` + dockerSocket + ` exec -it --workdir ` + newDevcontainer.DevcontainerWorkDir + "/" + repo.Name + `  ` + newDevcontainer.Name + ` sh -c 'echo "$WEB_TERMINAL_HELLO";bash'` + "\n",
		ListId:         4,
		DevcontainerId: newDevcontainer.Id,
	}); err != nil {
		log.Info("Failed to insert record: %v", err)
		return imageName, err
	}
	return imageName, nil
}
func IsContainerNotFound(ctx context.Context, containerName string) (bool, error) {
	cli, err := docker_module.CreateDockerClient(ctx)
	if err != nil {
		return false, err
	}
	defer cli.Close()
	containerID, err := docker_module.GetContainerID(cli, containerName)
	if err != nil {
		// 检查是否为 "未找到" 错误
		if errdefs.IsNotFound(err) {
			return true, nil
		}
		return false, err
	}
	_, err = cli.ContainerInspect(ctx, containerID)
	if err != nil {

		// 检查是否为 "未找到" 错误
		if docker_module.IsContainerNotFound(err) {
			return true, nil
		}
		// 其他类型的错误
		return false, err
	}
	// 无错误表示容器存在
	return false, nil
}
func DeleteDevContainerByDocker(ctx context.Context, devContainerName string) error {
	// 创建docker client
	cli, err := docker_module.CreateDockerClient(ctx)
	if err != nil {
		return err
	}
	defer cli.Close()
	// 获取容器 ID

	containerID, err := docker_module.GetContainerID(cli, devContainerName)
	if err != nil {
		if errdefs.IsNotFound(err) {
			return nil
		}
		return err
	}
	// 删除容器
	if err := docker_module.DeleteContainer(ctx, cli, containerID); err != nil {
		return err
	}
	return nil
}
func RestartDevContainerByDocker(ctx context.Context, devContainerName string) error {
	// 创建docker client
	cli, err := docker_module.CreateDockerClient(ctx)
	if err != nil {
		return err
	}
	defer cli.Close()
	// 获取容器 ID

	containerID, err := docker_module.GetContainerID(cli, devContainerName)
	if err != nil {
		return err
	}
	// restart容器
	timeout := 10 // 超时时间（秒）
	err = cli.ContainerRestart(context.Background(), containerID, container.StopOptions{
		Timeout: &timeout,
	})
	if err != nil {
		return err
	} else {
		log.Info("容器已重启")
	}
	return nil
}
func StopDevContainerByDocker(ctx context.Context, devContainerName string) error {
	// 创建docker client
	cli, err := docker_module.CreateDockerClient(ctx)
	if err != nil {
		return err
	}
	defer cli.Close()

	// 获取容器 ID
	containerID, err := docker_module.GetContainerID(cli, devContainerName)
	if err != nil {
		return err
	}
	// stop容器
	timeout := 10 // 超时时间（秒）
	err = cli.ContainerStop(context.Background(), containerID, container.StopOptions{
		Timeout: &timeout,
	})
	if err != nil {
		return err
	} else {
		log.Info("容器已停止")
	}
	return nil
}
func UpdateDevContainerByDocker(ctx context.Context, devContainerInfo *devcontainer_models.Devcontainer, updateInfo *UpdateInfo, repo *gitea_context.Repository, doer *user.User) error {
	// 创建docker client
	cli, err := docker_module.CreateDockerClient(ctx)
	if err != nil {
		return err
	}
	defer cli.Close()
	// update容器
	imageRef := updateInfo.RepositoryAddress + "/" + updateInfo.RepositoryUsername + "/" + updateInfo.ImageName
	configurationString, err := GetDevcontainerConfigurationString(ctx, repo.Repository)
	if err != nil {
		return err
	}
	configurationModel, err := UnmarshalDevcontainerConfigContent(configurationString)
	if err != nil {
		return err
	}

	if updateInfo.SaveMethod == "on" {

		// 创建构建上下文（包含Dockerfile的tar包）
		var buf bytes.Buffer
		tw := tar.NewWriter(&buf)
		defer tw.Close()
		// 添加Dockerfile到tar包
		var dockerfileContent string
		dockerfile := "Dockerfile"
		if configurationModel.Build == nil || configurationModel.Build.Dockerfile == "" {
			_, err := FileExists(".devcontainer/Dockerfile", repo)
			if err != nil {
				return err
			}
			dockerfileContent, err = GetFileContentByPath(ctx, repo.Repository, ".devcontainer/Dockerfile")
			if err != nil {
				return err
			}
		} else {
			_, err := FileExists(".devcontainer/"+configurationModel.Build.Dockerfile, repo)
			if err != nil {
				if git.IsErrNotExist(err) {
					_, err := FileExists(".devcontainer/Dockerfile", repo)
					if err != nil {
						return err
					}
					dockerfileContent, err = GetFileContentByPath(ctx, repo.Repository, ".devcontainer/Dockerfile")
					if err != nil {
						return err
					}
				}
				return err
			} else {
				dockerfileContent, err = GetFileContentByPath(ctx, repo.Repository, ".devcontainer/"+configurationModel.Build.Dockerfile)
				if err != nil {
					return err
				}
			}
		}

		content := []byte(dockerfileContent)
		header := &tar.Header{
			Name: dockerfile,
			Size: int64(len(content)),
			Mode: 0644,
		}
		if err := tw.WriteHeader(header); err != nil {
			return err
		}
		if _, err := tw.Write(content); err != nil {
			return err
		}
		buildOptions := types.ImageBuildOptions{
			Tags: []string{imageRef}, // 镜像标签
		}

		_, err = cli.ImageBuild(
			context.Background(),
			&buf,
			buildOptions,
		)
		if err != nil {
			log.Info(err.Error())
			return err
		}

	} else {
		// 获取容器 ID
		containerID, err := docker_module.GetContainerID(cli, devContainerInfo.Name)
		if err != nil {
			return err
		}
		// 提交容器
		_, err = cli.ContainerCommit(ctx, containerID, types.ContainerCommitOptions{Reference: imageRef})
		if err != nil {
			return err
		}
	}
	// 推送到仓库
	dockerHost, err := docker_module.GetDockerSocketPath()
	if err != nil {
		return err
	}
	err = docker_module.PushImage(dockerHost, updateInfo.RepositoryUsername, updateInfo.PassWord, updateInfo.RepositoryAddress, imageRef)
	if err != nil {
		return err
	}

	// 定义正则表达式来匹配 image 字段
	re := regexp.MustCompile(`"image"\s*:\s*"([^"]+)"`)
	// 使用正则表达式查找并替换 image 字段的值
	newConfiguration := re.ReplaceAllString(configurationString, `"image": "`+imageRef+`"`)
	err = UpdateDevcontainerConfiguration(newConfiguration, repo.Repository, doer)
	if err != nil {
		return err
	}
	return nil
}

// ImageExists 检查指定镜像是否存在
// 返回值：
// - bool: 镜像是否存在（true=存在，false=不存在）
// - error: 非空表示检查过程中发生错误
func ImageExists(ctx context.Context, imageName string) (bool, error) {
	// 创建 Docker 客户端
	cli, err := docker_module.CreateDockerClient(ctx)
	if err != nil {
		return false, err // 其他错误
	}
	// 获取镜像信息
	_, _, err = cli.ImageInspectWithRaw(ctx, imageName)
	if err != nil {
		if client.IsErrNotFound(err) {
			return false, nil // 镜像不存在，但不是错误
		}
		return false, err // 其他错误
	}
	return true, nil // 镜像存在
}

func CheckDirExistsFromDocker(ctx context.Context, containerName, dirPath string) (bool, error) {
	// 上下文
	// 创建 Docker 客户端
	cli, err := docker_module.CreateDockerClient(ctx)
	if err != nil {
		return false, err
	}
	// 获取容器 ID
	containerID, err := docker_module.GetContainerID(cli, containerName)
	if err != nil {
		return false, err
	}
	// 创建 exec 配置
	execConfig := types.ExecConfig{
		Cmd:          []string{"test", "-d", dirPath}, // 检查目录是否存在
		AttachStdout: true,
		AttachStderr: true,
	}
	// 创建 exec 实例
	execResp, err := cli.ContainerExecCreate(context.Background(), containerID, execConfig)
	if err != nil {
		return false, err
	}

	// 执行命令
	var exitCode int
	err = cli.ContainerExecStart(context.Background(), execResp.ID, types.ExecStartCheck{})
	if err != nil {
		return false, err
	}

	// 获取命令执行结果
	resp, err := cli.ContainerExecInspect(context.Background(), execResp.ID)
	if err != nil {
		return false, err
	}

	exitCode = resp.ExitCode
	return exitCode == 0, nil // 退出码为 0 表示目录存在
}

func CheckFileExistsFromDocker(ctx context.Context, containerName, filePath string) (bool, error) {
	// 上下文
	// 创建 Docker 客户端
	cli, err := docker_module.CreateDockerClient(ctx)
	if err != nil {
		return false, err
	}
	// 获取容器 ID
	containerID, err := docker_module.GetContainerID(cli, containerName)
	if err != nil {
		return false, err
	}
	// 创建 exec 配置
	execConfig := types.ExecConfig{
		Cmd:          []string{"test", "-e", filePath}, // 检查文件是否存在
		AttachStdout: true,
		AttachStderr: true,
	}

	// 创建 exec 实例
	execResp, err := cli.ContainerExecCreate(context.Background(), containerID, execConfig)
	if err != nil {
		return false, err
	}

	// 执行命令
	var exitCode int
	err = cli.ContainerExecStart(context.Background(), execResp.ID, types.ExecStartCheck{})
	if err != nil {
		return false, err
	}

	// 获取命令执行结果
	resp, err := cli.ContainerExecInspect(context.Background(), execResp.ID)
	if err != nil {
		return false, err
	}

	exitCode = resp.ExitCode
	return exitCode == 0, nil // 退出码为 0 表示目录存在
}
func RegistWebTerminal(ctx context.Context) error {
	log.Info("开始构建WebTerminal...")
	cli, err := docker_module.CreateDockerClient(ctx)
	if err != nil {
		return err
	}
	defer cli.Close()
	//拉取web_terminal镜像
	dockerHost, err := docker_module.GetDockerSocketPath()
	if err != nil {
		return fmt.Errorf("获取docker socket路径失败:%v", err)
	}
	// 拉取镜像
	err = docker_module.PullImage(ctx, cli, dockerHost, setting.DevContainerConfig.Web_Terminal_Image)
	if err != nil {
		fmt.Errorf("拉取web_terminal镜像失败:%v", err)
	}

	timestamp := time.Now().Format("20060102150405")
	binds := []string{
		"/var/run/docker.sock:/var/run/docker.sock",
	}
	containerName := "webterminal-" + timestamp
	//创建并启动WebTerminal容器
	err = docker_module.CreateAndStartContainer(ctx, cli, setting.DevContainerConfig.Web_Terminal_Image,
		nil,
		nil, binds,
		nat.PortSet{
			"7681/tcp": struct{}{},
		},
		containerName)
	if err != nil {
		return fmt.Errorf("创建并注册WebTerminal失败:%v", err)
	}
	// Save settings.
	cfg, err := setting.NewConfigProviderFromFile(setting.CustomConf)
	if err != nil {
		return err
	}
	_, err = docker_module.GetMappedPort(ctx, containerName, "7681")
	if err != nil {
		return err
	}
	cfg.Section("devcontainer").Key("WEB_TERMINAL_CONTAINER").SetValue(containerName)
	if err = cfg.SaveTo(setting.CustomConf); err != nil {
		return err
	}
	return nil
}

// ContainerExists 检查容器是否存在，返回存在状态和容器ID（如果存在）
func ContainerExists(ctx context.Context, containerName string) (bool, string, error) {
	cli, err := docker_module.CreateDockerClient(ctx)
	if err != nil {
		return false, "", err
	}
	// 设置过滤器，根据容器名称过滤
	filter := filters.NewArgs()
	filter.Add("name", containerName)

	// 获取容器列表，使用过滤器
	containers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{
		All:     true, // 包括所有容器（运行的和停止的）
		Filters: filter,
	})
	if err != nil {
		return false, "", err
	}

	// 遍历容器，检查名称是否完全匹配
	for _, container := range containers {
		for _, name := range container.Names {
			// 容器名称在Docker API中是以斜杠开头的，例如 "/my-container"
			// 所以我们需要检查去掉斜杠后的名称是否匹配
			if strings.TrimPrefix(name, "/") == containerName {
				return true, container.ID, nil
			}
		}
	}

	return false, "", nil
}
